package process

import (
	"encoding/json"
	"go_code/chatroom/server/message"
	"go_code/chatroom/server/model"
	"go_code/chatroom/server/utils"
	"net"
)

type UserProcess struct {
	Conn net.Conn
	UserId int64
	Username string
}

func (up *UserProcess) Register(msg *message.Message) (err error) {
	var registerReqMsg *message.RegisterReqMessage
	var registerResMsg *message.ResponseMessage

	_ = json.Unmarshal([]byte(msg.Data), &registerReqMsg)

	err = model.UserDaoObj.Register(registerReqMsg)
	if err != nil {
		if err == model.ErrUserExists {
			registerResMsg = &message.ResponseMessage{
				Code: 403,
				Error: err.Error(),
			}
		} else {
			registerResMsg = &message.ResponseMessage{
				Code: 500,
				Error: "服务器发生未知错误...",
			}
		}
	} else {
		registerResMsg = &message.ResponseMessage{
			Code: 200,
			Error: "注册成功",
		}
	}

	marshal, _ := json.Marshal(registerResMsg)

	msg = &message.Message{
		Type: message.TypeRegisterRes,
		Data: string(marshal),
	}

	t := utils.Transport{
		Conn: up.Conn,
	}

	_ = t.WritePkg(msg)

	return 
}

func (up *UserProcess) Login(msg *message.Message) (err error) {
	var loginReq *message.LoginReqMessage
	var loginRes = &message.LoginResMessage{}

	_ = json.Unmarshal([]byte(msg.Data), &loginReq)

	user, err := model.UserDaoObj.Login(loginReq)
	if err != nil {
		if err == model.ErrUserNotExists {
			loginRes.Code = 404
			loginRes.Error = err.Error()
		} else if err == model.ErrPassword {
			loginRes.Code = 403
			loginRes.Error = err.Error()
		} else {
			loginRes.Code = 500
			loginRes.Error = "服务器内部错误..."
		}
	} else {
		loginRes.Code = 200
		loginRes.Error = "登录成功"

		// 将登录成功的用户加入到 map
		// 分两种情况：
		// 1, 当自己登录时，服务端将自己加入到在线列表，通知别人（除去自己），并将在线用户 UserId 切片返回
		// 2, 当别人登录时，服务端将别人加入到在线列表，
		// 在 client 一直运行的协程（keepConnection方法）会接收通知消息，并更新我本地的这个人的在线状态
		up.UserId = user.UserId
		up.Username = user.Username
		userManager.AddOnlineUser(up)

		for userId := range userManager.onlineUsers {
			loginRes.UserIds = append(loginRes.UserIds, userId)
		}

		// 通知其他用户该用户已上线
		// 因为此方法中已经把自己过滤掉，所以这个消息不会发送给自己
		// 自己登录时接收的还是登录成功的信息
		up.notifyStatusToOtherUsers(user.UserId)
	}

	marshal, _ := json.Marshal(loginRes)
	msg = &message.Message{
		Type: message.TypeLoginReq,
		Data: string(marshal),
	}

	t := &utils.Transport{
		Conn: up.Conn,
	}

	err = t.WritePkg(msg)
	if err != nil {
		return
	}

	return
}

func (up *UserProcess) notifyStatusToOtherUsers(userId int64) {
	onlineUsers := userManager.GetAllOnlineUsers()

	for id, up := range onlineUsers {
		if userId == id {
			continue
		}

		up.notifyToOther(userId, up.Username)
	}
}

func (up *UserProcess) notifyToOther(userId int64, username string) {
	var msg *message.Message
	var userStatus *message.UserStatusMessage

	userStatus = &message.UserStatusMessage{
		User: message.User{
			UserId: userId,
			Username: username,
			Status: message.UserStatusOnline,
		},
	}

	marshal, _ := json.Marshal(userStatus)
	msg = &message.Message{
		Type: message.NotifyUserStatus,
		Data: string(marshal),
	}

	t := &utils.Transport{
		Conn: up.Conn,
	}

	err := t.WritePkg(msg)
	if err != nil {
		return
	}

	return
}
